#include "spi_lcd.hpp"
#include <cstring>
#include <cstdio>
#include <esp_log.h>
#include <freertos/task.h>
#include "esp_log.h"


/* LCD需要初始化一组命令/参数值。它们存储在此结构中  */
typedef struct
{
    uint8_t cmd;
    uint8_t data[16];
    uint8_t databytes; /* 数据中没有数据；比特7＝设置后的延迟；0xFF=cmds结束 */
} lcd_init_cmd_t;

spi_device_handle_t spi_dev_handle = { 0 };

LCDDisplay::LCDDisplay() {}

LCDDisplay::LCDDisplay(spi_host_device_t host, uint16_t wrPin, uint16_t csPin, uint16_t blPin, uint16_t rstPin)
    : _host(host), _wrPin(wrPin), _csPin(csPin), _blPin(blPin), _rstPin(rstPin) {
        instance = this;  // 在构造函数中设置实例指针
        this->_spi_lcd_handle = new spi_device_handle_t;
    }

LCDDisplay::~LCDDisplay() {}


LCDDisplay* LCDDisplay::instance = nullptr;

void LCDDisplay::init() {
    spi_device_interface_config_t devcfg = { 0 };
    printf("LCD start init\n");

    // 配置SPI设备
    devcfg = {
        .mode = 0,                  // SPI模式0
        .clock_speed_hz = 60 * 1000 * 1000,   // 10 MHz
        .spics_io_num = _csPin,     // CS引脚
        .queue_size = 7             // 传输队列大小
    };
    
    // 添加SPI设备
    esp_err_t ret = spi_bus_add_device(_host, &devcfg, &spi_dev_handle);
    if (ret != ESP_OK) {
        printf("Failed to add SPI device: %s\n", esp_err_to_name(ret));
        return;
    }
    _configGPIO(_wrPin, GPIO_MODE_OUTPUT, GPIO_PULLUP_ENABLE, GPIO_PULLDOWN_DISABLE, GPIO_INTR_DISABLE);
    _configGPIO(_blPin, GPIO_MODE_OUTPUT, GPIO_PULLUP_DISABLE, GPIO_PULLDOWN_ENABLE, GPIO_INTR_DISABLE);
    _configGPIO(_rstPin, GPIO_MODE_OUTPUT, GPIO_PULLUP_ENABLE, GPIO_PULLDOWN_DISABLE, GPIO_INTR_DISABLE);
    printf("LCD gpio finish init\n");
    lcd_off();
    lcd_hard_reset();
    initScreen();
    printf("LCD screen finish init\n");
    lcdOn();
    printf("lcd ON\n");

}

void LCDDisplay::initScreen() {
        /* 0.96寸lcd屏幕初始化序列 */
    lcd_init_cmd_t ili_init_cmds[] =
    {
        {0x11, {0}, 0x80},
        {0x21, {0}, 0x80},
        {0xB1, {0x05,0x3A,0x3A}, 3},
        {0xB2, {0x05,0x3A,0x3A}, 3},
        {0xB3, {0x05,0x3A,0x3A,0x05,0x3A,0x3A}, 6},
        {0xB4, {0x03}, 1},
        {0xC0, {0x62,0x02,0x04}, 3},
        {0xC1, {0xC0}, 1},
        {0xC2, {0x0D,0x00}, 2},
        {0xC3, {0x8D,0x6A}, 2},
        {0xC4, {0x8D,0xEE}, 2},
        {0xC5, {0x0E}, 1},
        {0xE0, {0x10, 0x0E, 0x02, 0x03, 0x0E, 0x07, 0x02, 0x07, 0x0A, 0x12, 0x27, 0x37, 0x00, 0x0D , 0x0E , 0x10}, 16},
        {0xE1, {0x10, 0x0E, 0x03, 0x03, 0x0F, 0x06, 0x02, 0x08, 0x0A, 0x13, 0x26, 0x36, 0x00, 0x0D , 0x0E , 0x10}, 16},
        {0x3A, {0x05}, 1},
        {0x36, {0xA8}, 1},
        {0x29, {0}, 0x80},
        {0, {0}, 0xff},
    };
    
    int cmd = 0;
    /* 发送初始化序列 */
    while (ili_init_cmds[cmd].databytes != 0xff) {
        lcdWriteCmd(ili_init_cmds[cmd].cmd);
        lcdWriteData(ili_init_cmds[cmd].data, ili_init_cmds[cmd].databytes & 0x1F);
        
        if (ili_init_cmds[cmd].databytes & 0x80) {
            vTaskDelay(120);
        }
        cmd++;
    }
}

void LCDDisplay::lcdOn() {
    gpio_set_level(LCD_NUM_BL, 1);
    vTaskDelay(10);
}


void LCDDisplay::lcd_off() {
    gpio_set_level(LCD_NUM_BL, 0);
    vTaskDelay(10);
}

void LCDDisplay::lcd_hard_reset() {
    gpio_set_level(LCD_NUM_RST, 0);
    vTaskDelay(200);
    gpio_set_level(LCD_NUM_RST, 1);
    vTaskDelay(200);
}

void LCDDisplay::_configGPIO(uint64_t pin_bit_mask, gpio_mode_t mode, gpio_pullup_t pull_up_en, gpio_pulldown_t pull_down_en, gpio_int_type_t intr_type) {
    gpio_config_t gpio_init_struct = { 0 };
    gpio_init_struct.intr_type = intr_type;                 /* 失能引脚中断 */
    gpio_init_struct.mode = mode;                       /* 配置输出模式 */
    gpio_init_struct.pin_bit_mask = 1ull << pin_bit_mask ;           /* 配置引脚位掩码 */
    gpio_init_struct.pull_down_en = pull_down_en;          /* 失能下拉 */
    gpio_init_struct.pull_up_en = pull_up_en;               /* 使能上拉 */
    gpio_config(&gpio_init_struct);   
}

void LCDDisplay::setSPIInterface(SPI spi_display) {
    controllerSPI = spi_display;
}


void LCDDisplay::lcdSetCursor(uint16_t xpos, uint16_t ypos) {
    uint8_t databuf[4] = {0};
    databuf[0] = (xpos + 1) >> 8;
    databuf[1] = 0xFF & (xpos + 1);
    databuf[2] = (xpos + 1) >> 8;
    databuf[3] = 0xFF & (xpos + 1);
    lcdWriteCmd(0x2A);
    lcdWriteData(databuf,4);

    databuf[0] = (ypos + 26) >> 8;
    databuf[1] = 0xFF & (ypos + 26);
    databuf[2] = (ypos + 26) >> 8;
    databuf[3] = 0xFF & (ypos + 26);
    lcdWriteCmd(0x2B);
    lcdWriteData(databuf,4);
    lcdWriteCmd(0x2C);
} 

void LCDDisplay::lcdWriteCmd(uint8_t cmd) {
    gpio_set_level(LCD_NUM_WR, 0);
    controllerSPI.spiWriteCmd(spi_dev_handle, cmd);
}

void LCDDisplay::lcdWriteData(const uint8_t *data, int len) {
    gpio_set_level(LCD_NUM_WR, 1);
    controllerSPI.spiWriteData(spi_dev_handle, data, len);
}

void LCDDisplay::lcdDrawPixel(uint16_t xpos, uint16_t ypos, uint16_t color) {
    lcdSetCursor(xpos, ypos);
    uint8_t data[2] = {0};
    data[0] = color >> 8;
    data[1] = 0xFF & color;
    lcdWriteData(data, 2);
}


void LCDDisplay::lcdSetWindow(uint16_t start_x, uint16_t end_x, uint16_t start_y, uint16_t end_y) {
    uint8_t databuf[4] = {0};
    databuf[0] = (start_x + 1) >> 8;
    databuf[1] = 0xFF & (start_x + 1);
    databuf[2] = (end_x + 1) >> 8;
    databuf[3] = 0xFF & (end_x + 1);
    lcdWriteCmd(0x2A);
    lcdWriteData(databuf, 4);

    databuf[0] = (start_y + 26) >> 8;
    databuf[1] = 0xFF & (start_y + 26);
    databuf[2] = (end_y + 26) >> 8;
    databuf[3] = 0xFF & (end_y + 26);
    lcdWriteCmd(0x2B);
    lcdWriteData(databuf, 4);

    lcdWriteCmd(0x2C);
}

void LCDDisplay::lcdPrintImage(uint16_t start_x, uint16_t end_x, uint16_t start_y, uint16_t end_y, uint16_t *buffer) {
    lcdSetWindow(start_x, end_x, start_y, end_y);

    for (uint16_t x = 0; x < end_x - start_x + 1; x++) {
        for (uint16_t y = 0; y< end_y - start_y + 1; y++) {
            uint8_t data[2] = {0};
            data[0] = *buffer>>8;
            data[1] = *buffer & 0xFF;
            lcdWriteData(data, 2);
            buffer++;
        }
    }
    lcdSetWindow(start_x, end_x, start_y, end_y);
}

void LCDDisplay::lcdDisplayFlushcb(lv_display_t* disp_drv, const lv_area_t* area, uint8_t * px_map) {
    int32_t x_start = area->x1;
    int32_t y_start = area->y1;

    int32_t x_end = area->x2;
    int32_t y_end = area->y2;

    lv_color16_t * buffer = (lv_color16_t *) px_map;
    instance->lcdSetWindow(area->x1, area->x2, area->y1, area->y2);
    for (int32_t x=x_start; x<=x_end; x++) {
        for (int32_t y=y_start; y<=y_end; y++) {
            uint16_t data = (buffer->red<<11) | (buffer->green << 5) | (buffer->blue);
            uint8_t writeData[2] = {0};
            writeData[0] = data >> 8;
            writeData[1] = data & 0xFF;
            instance->lcdWriteData(writeData, 2);
            buffer++;
        }
    }
    instance->lcdSetWindow(area->x1, area->x2, area->y1, area->y2);
    // 通知 LVGL 刷新完成
    ESP_LOGI("spi_lcd", "flush ok!");
    lv_disp_flush_ready(disp_drv);
}

void LCDDisplay::lcdClear(uint16_t color) {
    uint8_t data[2] = {0};

    data[0] = color >> 8;
    data[1] = color & 0xFF;
    
    for (int x=0; x< 160; x++) {
        for (int y=0; y<80; y++) {
            lcdDrawPixel(x, y, color);
        }
    }
}